<template>
  <div class="base-upload">
    <!-- 标签 -->
    <div v-if="label" class="base-upload__label">
      <span class="base-upload__label-text">{{ label }}</span>
      <span v-if="required" class="base-upload__required">*</span>
    </div>

    <!-- 上传区域 -->
    <van-uploader
      v-model="fileList"
      :accept="accept"
      :multiple="multiple"
      :disabled="disabled"
      :max-count="maxCount"
      :max-size="maxSize"
      :preview-size="previewSize"
      :preview-image="previewImage"
      :preview-full-image="previewFullImage"
      :delete-icon="deleteIcon"
      :show-upload="showUpload"
      :capture="capture"
      :after-read="handleAfterRead"
      :before-read="handleBeforeRead"
      :before-delete="handleBeforeDelete"
      :upload-icon="uploadIcon"
      :upload-text="uploadText"
      @oversize="handleOversize"
      @click-upload="handleClickUpload"
      @click-preview="handleClickPreview"
      @delete="handleDelete"
    >
      <!-- 自定义上传区域 -->
      <template v-if="$slots.default" #default>
        <slot />
      </template>

      <!-- 自定义预览 -->
      <template v-if="$slots.preview" #preview="{ file, index }">
        <slot name="preview" :file="file" :index="index" />
      </template>

      <!-- 自定义预览封面 -->
      <template v-if="$slots['preview-cover']" #preview-cover="{ file, index }">
        <slot name="preview-cover" :file="file" :index="index" />
      </template>
    </van-uploader>

    <!-- 错误信息 -->
    <div v-if="errorMessage" class="base-upload__error">
      {{ errorMessage }}
    </div>

    <!-- 提示信息 -->
    <div v-if="tip" class="base-upload__tip">
      {{ tip }}
    </div>

    <!-- 文件列表 -->
    <div v-if="showFileList && fileList.length > 0" class="base-upload__list">
      <div
        v-for="(file, index) in fileList"
        :key="index"
        class="base-upload__item"
      >
        <div class="base-upload__item-info">
          <van-icon :name="getFileIcon(file)" class="base-upload__item-icon" />
          <span class="base-upload__item-name">{{ file.name || file.url }}</span>
          <span class="base-upload__item-size">{{ formatFileSize(file.size) }}</span>
        </div>
        <div class="base-upload__item-actions">
          <van-icon
            v-if="file.status === 'uploading'"
            name="loading"
            class="base-upload__item-loading"
          />
          <van-icon
            v-else-if="file.status === 'failed'"
            name="warning-o"
            class="base-upload__item-error"
            @click="handleRetry(file, index)"
          />
          <van-icon
            v-else
            name="clear"
            class="base-upload__item-delete"
            @click="handleRemove(index)"
          />
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { ref, computed, watch, defineComponent } from 'vue'
import { uploadFile } from '@/api/upload'
import { formatFileSize } from '@/utils/formatData'

export default defineComponent({
  name: 'BaseUpload',
  props: {
    /**
     * 文件列表
     */
    modelValue: {
      type: Array,
      default: () => []
    },
    /**
     * 标签文本
     */
    label: {
      type: String,
      default: ''
    },
    /**
     * 是否必填
     */
    required: {
      type: Boolean,
      default: false
    },
    /**
     * 接受的文件类型
     */
    accept: {
      type: String,
      default: '*'
    },
    /**
     * 是否支持多选
     */
    multiple: {
      type: Boolean,
      default: false
    },
    /**
     * 是否禁用
     */
    disabled: {
      type: Boolean,
      default: false
    },
    /**
     * 最大上传数量
     */
    maxCount: {
      type: Number,
      default: Infinity
    },
    /**
     * 文件大小限制（字节）
     */
    maxSize: {
      type: Number,
      default: 10 * 1024 * 1024 // 10MB
    },
    /**
     * 预览图尺寸
     */
    previewSize: {
      type: [Number, String, Array],
      default: 80
    },
    /**
     * 是否在上传完成后展示预览图
     */
    previewImage: {
      type: Boolean,
      default: true
    },
    /**
     * 是否在点击预览图后展示全屏图片预览
     */
    previewFullImage: {
      type: Boolean,
      default: true
    },
    /**
     * 删除图标名称
     */
    deleteIcon: {
      type: String,
      default: 'cross'
    },
    /**
     * 是否展示上传区域
     */
    showUpload: {
      type: Boolean,
      default: true
    },
    /**
     * 图片选取模式
     */
    capture: {
      type: String,
      default: ''
    },
    /**
     * 上传区域图标名称
     */
    uploadIcon: {
      type: String,
      default: 'plus'
    },
    /**
     * 上传区域文字提示
     */
    uploadText: {
      type: String,
      default: ''
    },
    /**
     * 错误信息
     */
    errorMessage: {
      type: String,
      default: ''
    },
    /**
     * 提示信息
     */
    tip: {
      type: String,
      default: ''
    },
    /**
     * 是否显示文件列表
     */
    showFileList: {
      type: Boolean,
      default: false
    },
    /**
     * 上传方法
     */
    uploadMethod: {
      type: Function,
      default: null
    },
    /**
     * 上传参数
     */
    uploadData: {
      type: Object,
      default: () => ({})
    },
    /**
     * 上传请求头
     */
    uploadHeaders: {
      type: Object,
      default: () => ({})
    },
    /**
     * 是否自动上传
     */
    autoUpload: {
      type: Boolean,
      default: true
    }
  },
  emits: ['update:modelValue', 'change', 'success', 'error', 'progress', 'remove'],
  setup(props, { emit }) {
    /**
     * 文件列表
     */
    const fileList = computed({
      get: () => props.modelValue,
      set: (value) => emit('update:modelValue', value)
    })

    /**
     * 读取文件前的回调
     */
    const handleBeforeRead = (file) => {
      // 文件类型验证
      if (props.accept !== '*' && !validateFileType(file, props.accept)) {
        showToast('文件类型不支持')
        return false
      }

      // 文件大小验证
      if (file.size > props.maxSize) {
        showToast(`文件大小不能超过 ${formatFileSize(props.maxSize)}`)
        return false
      }

      return true
    }

    /**
     * 读取文件后的回调
     */
    const handleAfterRead = (file) => {
      if (Array.isArray(file)) {
        file.forEach(item => processFile(item))
      } else {
        processFile(file)
      }
    }

    /**
     * 处理文件
     */
    const processFile = async (file) => {
      // 设置上传状态
      file.status = 'uploading'
      file.message = '上传中...'

      if (props.autoUpload) {
        try {
          const result = await uploadFileToServer(file)
          file.status = 'done'
          file.url = result.url
          file.message = ''
          emit('success', file, result)
        } catch (error) {
          file.status = 'failed'
          file.message = error.message || '上传失败'
          emit('error', file, error)
        }
      }

      emit('change', fileList.value)
    }

    /**
     * 上传文件到服务器
     */
    const uploadFileToServer = async (file) => {
      const uploadFn = props.uploadMethod || uploadFile
      
      const formData = new FormData()
      formData.append('file', file.file)
      
      // 添加额外参数
      Object.keys(props.uploadData).forEach(key => {
        formData.append(key, props.uploadData[key])
      })

      return await uploadFn(formData, {
        headers: props.uploadHeaders,
        onUploadProgress: (progressEvent) => {
          const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total)
          file.progress = progress
          emit('progress', file, progress)
        }
      })
    }

    /**
     * 文件超出大小限制
     */
    const handleOversize = (file) => {
      showToast(`文件大小不能超过 ${formatFileSize(props.maxSize)}`)
    }

    /**
     * 点击上传区域
     */
    const handleClickUpload = () => {
      // 可以在这里添加自定义逻辑
    }

    /**
     * 点击预览图
     */
    const handleClickPreview = (file, index) => {
      // 可以在这里添加自定义预览逻辑
    }

    /**
     * 删除文件前的回调
     */
    const handleBeforeDelete = (file, detail) => {
      return new Promise((resolve) => {
        showConfirmDialog({
          title: '确认删除',
          message: '确定要删除这个文件吗？'
        }).then(() => {
          resolve(true)
        }).catch(() => {
          resolve(false)
        })
      })
    }

    /**
     * 删除文件
     */
    const handleDelete = (file, detail) => {
      emit('remove', file, detail.index)
      emit('change', fileList.value)
    }

    /**
     * 手动删除文件
     */
    const handleRemove = (index) => {
      const newList = [...fileList.value]
      const removedFile = newList.splice(index, 1)[0]
      fileList.value = newList
      emit('remove', removedFile, index)
      emit('change', fileList.value)
    }

    /**
     * 重试上传
     */
    const handleRetry = async (file, index) => {
      file.status = 'uploading'
      file.message = '上传中...'

      try {
        const result = await uploadFileToServer(file)
        file.status = 'done'
        file.url = result.url
        file.message = ''
        emit('success', file, result)
      } catch (error) {
        file.status = 'failed'
        file.message = error.message || '上传失败'
        emit('error', file, error)
      }

      emit('change', fileList.value)
    }

    /**
     * 验证文件类型
     */
    const validateFileType = (file, accept) => {
      const acceptTypes = accept.split(',').map(type => type.trim())
      const fileType = file.type
      const fileName = file.name

      return acceptTypes.some(type => {
        if (type.startsWith('.')) {
          // 扩展名匹配
          return fileName.toLowerCase().endsWith(type.toLowerCase())
        } else if (type.includes('*')) {
          // MIME类型通配符匹配
          const regex = new RegExp(type.replace('*', '.*'))
          return regex.test(fileType)
        } else {
          // 精确MIME类型匹配
          return fileType === type
        }
      })
    }

    /**
     * 获取文件图标
     */
    const getFileIcon = (file) => {
      const fileType = file.type || ''
      const fileName = file.name || ''

      if (fileType.startsWith('image/')) {
        return 'photo-o'
      } else if (fileType.startsWith('video/')) {
        return 'video-o'
      } else if (fileType.startsWith('audio/')) {
        return 'music-o'
      } else if (fileType.includes('pdf') || fileName.endsWith('.pdf')) {
        return 'description'
      } else if (fileType.includes('word') || fileName.match(/\.(doc|docx)$/i)) {
        return 'description'
      } else if (fileType.includes('excel') || fileName.match(/\.(xls|xlsx)$/i)) {
        return 'description'
      } else if (fileType.includes('powerpoint') || fileName.match(/\.(ppt|pptx)$/i)) {
        return 'description'
      } else {
        return 'description'
      }
    }

    return {
      fileList,
      handleBeforeRead,
      handleAfterRead,
      handleOversize,
      handleClickUpload,
      handleClickPreview,
      handleBeforeDelete,
      handleDelete,
      handleRemove,
      handleRetry,
      getFileIcon,
      formatFileSize
    }
  }
})
</script>

<style lang="scss" scoped>
.base-upload {
  &__label {
    display: flex;
    align-items: center;
    margin-bottom: 8px;
    font-size: 14px;
    line-height: 20px;
    color: #323233;
  }

  &__label-text {
    flex: 1;
  }

  &__required {
    margin-left: 4px;
    color: #ee0a24;
  }

  &__error {
    margin-top: 4px;
    font-size: 12px;
    line-height: 16px;
    color: #ee0a24;
  }

  &__tip {
    margin-top: 8px;
    font-size: 12px;
    line-height: 16px;
    color: #969799;
  }

  &__list {
    margin-top: 16px;
  }

  &__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px;
    margin-bottom: 8px;
    background-color: #f7f8fa;
    border-radius: 6px;

    &:last-child {
      margin-bottom: 0;
    }
  }

  &__item-info {
    display: flex;
    align-items: center;
    flex: 1;
    min-width: 0;
  }

  &__item-icon {
    margin-right: 8px;
    font-size: 16px;
    color: #969799;
  }

  &__item-name {
    flex: 1;
    margin-right: 8px;
    font-size: 14px;
    color: #323233;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  &__item-size {
    font-size: 12px;
    color: #969799;
  }

  &__item-actions {
    display: flex;
    align-items: center;
    margin-left: 8px;
  }

  &__item-loading {
    font-size: 16px;
    color: #1989fa;
    animation: van-rotate 0.8s linear infinite;
  }

  &__item-error {
    font-size: 16px;
    color: #ee0a24;
    cursor: pointer;
  }

  &__item-delete {
    font-size: 16px;
    color: #c8c9cc;
    cursor: pointer;

    &:hover {
      color: #ee0a24;
    }
  }
}

// 全局样式覆盖
:global(.van-uploader) {
  .van-uploader__upload {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 80px;
    height: 80px;
    margin: 0 8px 8px 0;
    background-color: #f7f8fa;
    border: 1px dashed #dcdee0;
    border-radius: 6px;
    cursor: pointer;
    transition: border-color 0.2s;

    &:hover {
      border-color: #1989fa;
    }

    &:active {
      border-color: #0570db;
    }
  }

  .van-uploader__upload-icon {
    font-size: 24px;
    color: #dcdee0;
  }

  .van-uploader__upload-text {
    margin-top: 8px;
    font-size: 12px;
    color: #969799;
  }

  .van-uploader__preview {
    position: relative;
    margin: 0 8px 8px 0;
    cursor: pointer;
  }

  .van-uploader__preview-image {
    display: block;
    width: 80px;
    height: 80px;
    border-radius: 6px;
    object-fit: cover;
  }

  .van-uploader__preview-delete {
    position: absolute;
    top: -8px;
    right: -8px;
    width: 18px;
    height: 18px;
    background-color: #ee0a24;
    border-radius: 50%;
    cursor: pointer;
  }

  .van-uploader__preview-delete-icon {
    position: absolute;
    top: 50%;
    left: 50%;
    font-size: 10px;
    color: #fff;
    transform: translate(-50%, -50%);
  }

  .van-uploader__mask {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: #fff;
    background-color: rgba(50, 50, 51, 0.88);
    border-radius: 6px;
  }

  .van-uploader__mask-icon {
    font-size: 22px;
  }

  .van-uploader__mask-message {
    margin-top: 6px;
    padding: 0 4px;
    font-size: 12px;
    line-height: 14px;
  }

  .van-uploader__loading {
    width: 22px;
    height: 22px;
    color: #fff;
  }
}
</style>